﻿/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#include "CubismRenderState_D3D11.hpp"

#include "CubismRenderer_D3D11.hpp"
#include "CubismShader_D3D11.hpp"
#include "Type/csmVector.hpp"

//------------ LIVE2D NAMESPACE ------------
namespace Live2D
{
    namespace Cubism
    {
        namespace Framework
        {
            namespace Rendering
            {

                CubismRenderState_D3D11::CubismRenderState_D3D11()
                {
                    //
                    memset(_stored._valid, 0, sizeof(_stored._valid));

                    Create(CubismRenderer_D3D11::GetCurrentDevice());
                }

                CubismRenderState_D3D11::~CubismRenderState_D3D11()
                {
                    _pushed.Clear();

                    // サンプラーステート解放
                    for (csmUint32 i = 0; i < _samplerState.GetSize(); i++)
                    {
                        ID3D11SamplerState *sampl = _samplerState[i];
                        if (sampl)
                        {
                            sampl->Release();
                        }
                    }
                    _samplerState.Clear();

                    // Z解放
                    for (csmUint32 i = 0; i < _depthStencilState.GetSize(); i++)
                    {
                        ID3D11DepthStencilState *depth = _depthStencilState[i];
                        if (depth)
                        {
                            depth->Release();
                        }
                    }
                    _depthStencilState.Clear();

                    // ラスタライザ解放
                    for (csmUint32 i = 0; i < _rasterizeStateObjects.GetSize(); i++)
                    {
                        ID3D11RasterizerState *raster = _rasterizeStateObjects[i];
                        if (raster)
                        {
                            raster->Release();
                        }
                    }
                    _rasterizeStateObjects.Clear();

                    // ブレンドステート解放
                    for (csmUint32 i = 0; i < _blendStateObjects.GetSize(); i++)
                    {
                        ID3D11BlendState *state = _blendStateObjects[i];
                        if (state)
                        {
                            state->Release();
                        }
                    }
                    _blendStateObjects.Clear();
                }

                void CubismRenderState_D3D11::Create(ID3D11Device *device)
                {
                    if (!device)
                    {
                        return;
                    }

                    // Blend
                    ID3D11BlendState *state = NULL;
                    D3D11_BLEND_DESC blendDesc;
                    memset(&blendDesc, 0, sizeof(blendDesc));

                    // origin
                    _blendStateObjects.PushBack(NULL);

                    // ゼロ
                    blendDesc.AlphaToCoverageEnable = FALSE;
                    blendDesc.IndependentBlendEnable = FALSE; // falseの場合はRenderTarget[0]しか使用しなくなる
                    blendDesc.RenderTarget[0].BlendEnable = TRUE;
                    blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
                    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
                    blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
                    device->CreateBlendState(&blendDesc, &state);
                    _blendStateObjects.PushBack(state);

                    // 通常
                    blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
                    blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
                    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
                    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
                    device->CreateBlendState(&blendDesc, &state);
                    _blendStateObjects.PushBack(state);

                    // 加算
                    blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
                    blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
                    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
                    device->CreateBlendState(&blendDesc, &state);
                    _blendStateObjects.PushBack(state);

                    // 乗算
                    blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_COLOR;
                    blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
                    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
                    device->CreateBlendState(&blendDesc, &state);
                    _blendStateObjects.PushBack(state);

                    // マスク
                    blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_COLOR;
                    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
                    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
                    device->CreateBlendState(&blendDesc, &state);
                    _blendStateObjects.PushBack(state);

                    // ラスタライザ
                    ID3D11RasterizerState *rasterizer = NULL;
                    D3D11_RASTERIZER_DESC rasterDesc;
                    memset(&rasterDesc, 0, sizeof(rasterDesc));

                    // origin
                    _rasterizeStateObjects.PushBack(NULL);

                    // None
                    rasterDesc.FillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
                    rasterDesc.CullMode = D3D11_CULL_MODE::D3D11_CULL_NONE; // カリング無し
                    rasterDesc.FrontCounterClockwise = TRUE;                // CCWを表面にする
                    rasterDesc.DepthClipEnable = FALSE;
                    rasterDesc.MultisampleEnable = FALSE;
                    rasterDesc.DepthBiasClamp = 0;
                    rasterDesc.SlopeScaledDepthBias = 0;
                    device->CreateRasterizerState(&rasterDesc, &rasterizer);
                    _rasterizeStateObjects.PushBack(rasterizer);

                    // CCW
                    rasterDesc.CullMode = D3D11_CULL_MODE::D3D11_CULL_BACK; // 裏面を切る
                    device->CreateRasterizerState(&rasterDesc, &rasterizer);
                    _rasterizeStateObjects.PushBack(rasterizer);

                    // Depth
                    ID3D11DepthStencilState *depth = NULL;
                    D3D11_DEPTH_STENCIL_DESC depthDesc;
                    memset(&depthDesc, 0, sizeof(depthDesc));

                    // origin
                    _depthStencilState.PushBack(NULL);

                    // 無効
                    depthDesc.DepthEnable = false;
                    depthDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
                    depthDesc.DepthFunc = D3D11_COMPARISON_LESS;
                    depthDesc.StencilEnable = false;
                    device->CreateDepthStencilState(&depthDesc, &depth);
                    _depthStencilState.PushBack(depth);

                    // 有効
                    depthDesc.DepthEnable = true;
                    depthDesc.StencilEnable = false;
                    device->CreateDepthStencilState(&depthDesc, &depth);
                    _depthStencilState.PushBack(depth);

                    // Sampler
                    ID3D11SamplerState *sampler = NULL;
                    D3D11_SAMPLER_DESC samplerDesc;
                    memset(&samplerDesc, 0, sizeof(D3D11_SAMPLER_DESC));

                    // origin
                    _samplerState.PushBack(NULL);

                    // normal
                    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
                    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
                    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
                    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
                    samplerDesc.MaxAnisotropy = 1;
                    samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
                    samplerDesc.MinLOD = -D3D11_FLOAT32_MAX;
                    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
                    samplerDesc.BorderColor[0] = samplerDesc.BorderColor[1] = samplerDesc.BorderColor[2] = samplerDesc.BorderColor[3] = 1.0f;
                    device->CreateSamplerState(&samplerDesc, &sampler);
                    _samplerState.PushBack(sampler);
                }

                void CubismRenderState_D3D11::StartFrame()
                {
                    memset(_stored._valid, 0, sizeof(_stored._valid));

                    _pushed.Clear();
                }

                void CubismRenderState_D3D11::Save()
                {
                    // 現時点のステートをPush
                    _pushed.PushBack(_stored);
                }

                void CubismRenderState_D3D11::Restore(ID3D11DeviceContext *renderContext)
                {
                    const csmUint32 size = _pushed.GetSize();

                    if (size == 0)
                    {
                        return;
                    }

                    // forで辿って最後に設定した個所までチェック
                    csmBool isSet[State_Max];
                    memset(isSet, 0, sizeof(isSet));

                    for (csmInt32 i = static_cast<csmInt32>(_pushed.GetSize()) - 1; i >= 0; i--)
                    {
                        Stored &current = _pushed[i];

                        if (_pushed[i]._valid[State_Blend] && !isSet[State_Blend])
                        {
                            SetBlend(renderContext, current._blendState, current._blendFactor, current._blendMask, true);
                            isSet[State_Blend] = true;
                        }
                        if (_pushed[i]._valid[State_CullMode] && !isSet[State_CullMode])
                        {
                            SetCullMode(renderContext, current._cullMode, true);
                            isSet[State_CullMode] = true;
                        }
                        if (_pushed[i]._valid[State_Viewport] && !isSet[State_Viewport])
                        {
                            SetViewport(renderContext, current._viewportX, current._viewportY, current._viewportWidth, current._viewportHeight,
                                        current._viewportMinZ, current._viewportMaxZ, true);
                            isSet[State_Viewport] = true;
                        }
                        if (_pushed[i]._valid[State_ZEnable] && !isSet[State_ZEnable])
                        {
                            SetZEnable(renderContext, current._depthEnable, current._depthRef, true);
                            isSet[State_ZEnable] = true;
                        }
                        if (_pushed[i]._valid[State_Sampler] && !isSet[State_Sampler])
                        {
                            SetSampler(renderContext, current._sampler, true);
                            isSet[State_Sampler] = true;
                        }
                    }

                    Stored store = _pushed[size - 1];
                    _pushed.Remove(size - 1);
                    if (_pushed.GetSize() == 0)
                    {
                        _pushed.Clear();
                    }
                    _stored = store;
                }

                void CubismRenderState_D3D11::SetBlend(ID3D11DeviceContext *renderContext, Blend blendState, DirectX::XMFLOAT4 blendFactor,
                                                       UINT mask, csmBool force)
                {
                    if (!renderContext || blendState < 0 || Blend_Max <= blendState)
                    { // パラメータ異常チェック
                        return;
                    }

                    if (!_stored._valid[State_Blend] || force || _stored._blendFactor.x != blendFactor.x ||
                        _stored._blendFactor.y != blendFactor.y || _stored._blendFactor.z != blendFactor.z ||
                        _stored._blendFactor.w != blendFactor.w || _stored._blendMask != mask || _stored._blendState != blendState)
                    {
                        const float factor[4] = { blendFactor.x, blendFactor.y, blendFactor.z, blendFactor.w };
                        renderContext->OMSetBlendState(_blendStateObjects[blendState], factor, mask);
                    }

                    _stored._blendState = blendState;
                    _stored._blendFactor = blendFactor;
                    _stored._blendMask = mask;

                    _stored._valid[State_Blend] = true;
                }

                void CubismRenderState_D3D11::SetCullMode(ID3D11DeviceContext *renderContext, Cull cullFace, csmBool force)
                {
                    if (!renderContext || cullFace < 0 || Cull_Max <= cullFace)
                    { // パラメータ異常チェック
                        return;
                    }

                    if (!_stored._valid[State_CullMode] || force || _stored._cullMode != cullFace)
                    {
                        renderContext->RSSetState(_rasterizeStateObjects[cullFace]);
                    }

                    _stored._cullMode = cullFace;

                    _stored._valid[State_CullMode] = true;
                }

                void CubismRenderState_D3D11::SetViewport(ID3D11DeviceContext *renderContext, FLOAT left, FLOAT top, FLOAT width, FLOAT height,
                                                          FLOAT zMin, FLOAT zMax, csmBool force)
                {
                    if (!_stored._valid[State_Blend] || force || _stored._viewportX != left || _stored._viewportY != top ||
                        _stored._viewportWidth != width || _stored._viewportHeight != height || _stored._viewportMinZ != zMin ||
                        _stored._viewportMaxZ != zMax)
                    {
                        // コンテキストにセット
                        D3D11_VIEWPORT setViewport;
                        setViewport.TopLeftX = left;
                        setViewport.TopLeftY = top;
                        setViewport.Width = width;
                        setViewport.Height = height;
                        setViewport.MinDepth = zMin;
                        setViewport.MaxDepth = zMax;
                        renderContext->RSSetViewports(1, &setViewport);
                    }

                    _stored._viewportX = left;
                    _stored._viewportY = top;
                    _stored._viewportWidth = width;
                    _stored._viewportHeight = height;
                    _stored._viewportMinZ = zMin;
                    _stored._viewportMaxZ = zMax;

                    _stored._valid[State_Viewport] = true;
                }

                void CubismRenderState_D3D11::SetZEnable(ID3D11DeviceContext *renderContext, Depth enable, UINT stelcilRef, csmBool force)
                {
                    if (!renderContext || enable < 0 || Depth_Max <= enable)
                    { // パラメータ異常チェック
                        return;
                    }

                    if (!_stored._valid[State_ZEnable] || force || _stored._depthEnable != enable)
                    {
                        renderContext->OMSetDepthStencilState(_depthStencilState[enable], stelcilRef);
                    }

                    _stored._depthEnable = enable;
                    _stored._depthRef = stelcilRef;

                    _stored._valid[State_ZEnable] = true;
                }

                void CubismRenderState_D3D11::SetSampler(ID3D11DeviceContext *renderContext, Sampler sample, csmBool force)
                {
                    if (!renderContext || sample < 0 || Sampler_Max <= sample)
                    { // パラメータ異常チェック
                        return;
                    }

                    if (!_stored._valid[State_ZEnable] || force || _stored._sampler != sample)
                    {
                        // 0番だけ使用している
                        renderContext->PSSetSamplers(0, 1, &_samplerState[sample]);
                    }

                    _stored._sampler = sample;

                    _stored._valid[State_Sampler] = true;
                }

                void CubismRenderState_D3D11::SaveCurrentNativeState(ID3D11Device *device, ID3D11DeviceContext *renderContext)
                {
                    // まずは全破棄
                    _pushed.Clear();
                    // 未設定扱いに
                    memset(_stored._valid, 0, sizeof(_stored._valid));

                    // ここで取得したオブジェクトはRestoreNativeStateでリリース
                    // Blend
                    ID3D11BlendState *originBlend;
                    FLOAT originFactor[4];
                    UINT originMask;
                    renderContext->OMGetBlendState(&originBlend, originFactor, &originMask);
                    _blendStateObjects[Blend_Origin] = originBlend;                                           // と入れた上で
                    SetBlend(renderContext, Blend_Origin, DirectX::XMFLOAT4(originFactor), originMask, true); // コールする

                    // Cull
                    ID3D11RasterizerState *originRaster;
                    renderContext->RSGetState(&originRaster);
                    _rasterizeStateObjects[Cull_Origin] = originRaster; // と入れた上で
                    SetCullMode(renderContext, Cull_Origin, true);      // コールする

                    // Depth
                    ID3D11DepthStencilState *originDepth;
                    UINT stelcilRef;
                    renderContext->OMGetDepthStencilState(&originDepth, &stelcilRef);
                    _depthStencilState[Depth_Origin] = originDepth;            // と入れた上で
                    SetZEnable(renderContext, Depth_Origin, stelcilRef, true); // コールする

                    // Sampler
                    ID3D11SamplerState *originSampler;
                    renderContext->PSGetSamplers(0, 1, &originSampler);
                    _samplerState[Sampler_Origin] = originSampler;   // と入れた上で
                    SetSampler(renderContext, Sampler_Origin, true); // コールする

                    // ViewPortはオブジェクトではない
                    UINT numViewport = 1; // 取得は一つでOK
                    D3D11_VIEWPORT viewPort;
                    renderContext->RSGetViewports(&numViewport, &viewPort);
                    SetViewport(renderContext, viewPort.TopLeftX, viewPort.TopLeftY, viewPort.Width, viewPort.Height, viewPort.MinDepth,
                                viewPort.MaxDepth, true);

                    // 最後に上記の値を保存
                    Save();
                }

                void CubismRenderState_D3D11::RestoreNativeState(ID3D11Device *device, ID3D11DeviceContext *renderContext)
                {
                    // 全て再現
                    for (csmInt32 i = static_cast<csmInt32>(_pushed.GetSize()) - 1; i >= 0; i--)
                    {
                        Restore(renderContext);
                    }

                    // 各オリジナルオブジェクトはここで関わりをなくする
                    if (_samplerState[Sampler_Origin])
                    {
                        _samplerState[Sampler_Origin]->Release();
                        _samplerState[Sampler_Origin] = NULL;
                    }
                    if (_depthStencilState[Depth_Origin])
                    {
                        _depthStencilState[Depth_Origin]->Release();
                        _depthStencilState[Depth_Origin] = NULL;
                    }
                    if (_rasterizeStateObjects[Cull_Origin])
                    {
                        _rasterizeStateObjects[Cull_Origin]->Release();
                        _rasterizeStateObjects[Cull_Origin] = NULL;
                    }
                    if (_blendStateObjects[Blend_Origin])
                    {
                        _blendStateObjects[Blend_Origin]->Release();
                        _blendStateObjects[Blend_Origin] = NULL;
                    }
                }

            } // namespace Rendering
        }     // namespace Framework
    }         // namespace Cubism
} // namespace Live2D

//------------ LIVE2D NAMESPACE ------------
